#pragma once
#include <vector>

#define STBI_HEADER_FILE_ONLY
//#include "stb_image.c"
#include <sstream>
#include "IL/il.h"
#pragma comment (lib, "DevIL.lib")
#include "Vector3.h"
 
#ifdef _DEBUG
#define trace( text )\
{\
	std::wstringstream stringBuilder;\
	stringBuilder << text << std::endl;\
	OutputDebugString(stringBuilder.str().c_str());\
}
#endif
using namespace NBE;
namespace banknamespace
{
	

	//template<typename Real>
	//struct vec3
	//{
	//public:
	//	Real x, y, z;
	//	explicit vec3( Real x_ = 0, Real y_ = 0, Real z_ = 0) : x( x_ ), y( y_ ), z( z_ ) {}

	//	vec3& operator+=( const vec3& other )
	//	{
	//		x += other.x;
	//		y += other.y;
	//		z += other.z;

	//		return *this;
	//	}

	//	vec3 operator+( vec3& other ) const
	//	{
	//		vec3 result( *this );
	//		result += other;
	//		return result;
	//	}

	//	Real operator[]( int i ) const
	//	{
	//		assert( 0 <= i && i < 3 );
	//		return (&x) [i];

	//		/*switch(i)
	//		{
	//		case 0:
	//			return x;
	//		case 1:
	//			return y;
	//		case 2:
	//			return z;
	//		default:
	//			assert(false);
	//			return x;
	//		}*/
	//	}

	//	Real& operator[]( int i ) 
	//	{
	//		switch(i)
	//		{
	//		case 0:
	//			return x;
	//		case 1:
	//			return y;
	//		case 2:
	//			return z;
	//		default:
	//			assert(false);
	//			return x;
	//		}
	//	}

	//	vec3& operator-=( const vec3& other )
	//	{
	//		x -= other.x;
	//		y -= other.y;
	//		z -= other.z;

	//		return *this;
	//	}

	//	vec3 operator-( vec3& other ) const
	//	{
	//		vec3 result( *this );
	//		result -= other;
	//		return result;
	//	}

	//	bool operator==( const vec3& other )
	//	{
	//		bool res = false;
	//		if (x == other.x && y == other.y && z == other.z) res = true;

	//		return res;
	//	}

	//	vec3& operator*=( Real scalar )
	//	{
	//		x *= scalar;
	//		y *= scalar;
	//		z *= scalar;

	//		return *this;
	//	}

	//	vec3 operator*( Real scalar ) const
	//	{
	//		vec3 result( *this );
	//		result *= scalar;
	//		return result;
	//	}

	//	vec3& operator/=( Real scalar )
	//	{
	//		x /= scalar;
	//		y /= scalar;
	//		z /= scalar;

	//		return *this;
	//	}

	//	vec3 operator/( Real scalar ) const
	//	{
	//		vec3 result( *this );
	//		result /= scalar;
	//		return result;
	//	}
	//};
	//typedef vec3<float> vec3f;

	enum shaderAttribute
	{
		SATT_POSITION,
		SATT_TEXCOORD0,
		SATT_TEXCOORD1,
		SATT_NORMAL,
		SATT_TANGENT,
		SATT_BITANGENT,
		SATT_TANGENT4F
	};

	enum shaderAttrType
	{
		S_FLOAT4,
		S_FLOAT3,
		S_FLOAT2
	};

	enum shaderType
	{
		S_MAT4,
		S_VEC4,
		S_VEC3,
		S_FLOAT,
		S_TEXTURE
	};

	enum bufferType
	{
		B_INDEXBUFFER,
		B_VERTEXBUFER
	};

	struct attrStruct
	{
		const char* name;
		int id;
		shaderAttrType attrType;
		int offset;
	};

// 	struct vec3f
// 	{
// 	public:
// 		float x, y, z;
// 		explicit vec3f( float x_ = 0, float y_ = 0, float z_ = 0) : x( x_ ), y( y_ ), z( z_ ) {}
// 
// 		vec3f& operator+=( const vec3f& other )
// 		{
// 			x += other.x;
// 			y += other.y;
// 			z += other.z;
// 
// 			return *this;
// 		}
// 
// 		vec3f operator+( vec3f& other ) const
// 		{
// 			vec3f result( *this );
// 			result += other;
// 			return result;
// 		}
// 
// 		float operator[]( int i ) const
// 		{
// 			assert( 0 <= i && i < 3 );
// 			return (&x) [i];
// 
// 			/*switch(i)
// 			{
// 			case 0:
// 				return x;
// 			case 1:
// 				return y;
// 			case 2:
// 				return z;
// 			default:
// 				assert(false);
// 				return x;
// 			}*/
// 		}
// 
// 		float& operator[]( int i ) 
// 		{
// 			switch(i)
// 			{
// 			case 0:
// 				return x;
// 			case 1:
// 				return y;
// 			case 2:
// 				return z;
// 			default:
// 				assert(false);
// 				return x;
// 			}
// 		}
// 
// 		vec3f& operator-=( const vec3f& other )
// 		{
// 			x -= other.x;
// 			y -= other.y;
// 			z -= other.z;
// 
// 			return *this;
// 		}
// 
// 		vec3f operator-( vec3f& other ) const
// 		{
// 			vec3f result( *this );
// 			result -= other;
// 			return result;
// 		}
// 
// 		bool operator==( const vec3f& other )
// 		{
// 			bool res = false;
// 			if (x == other.x && y == other.y && z == other.z) res = true;
// 
// 			return res;
// 		}
// 
// 		vec3f& operator*=( float scalar )
// 		{
// 			x *= scalar;
// 			y *= scalar;
// 			z *= scalar;
// 
// 			return *this;
// 		}
// 
// 		vec3f operator*( float scalar ) const
// 		{
// 			vec3f result( *this );
// 			result *= scalar;
// 			return result;
// 		}
// 
// 		vec3f& operator/=( float scalar )
// 		{
// 			x /= scalar;
// 			y /= scalar;
// 			z /= scalar;
// 
// 			return *this;
// 		}
// 
// 		vec3f operator/( float scalar ) const
// 		{
// 			vec3f result( *this );
// 			result /= scalar;
// 			return result;
// 		}
// 	};
// 
// 	struct vec4f
// 	{
// 	public:
// 		float x, y, z, w;
// 		explicit vec4f( float x_ = 0, float y_ = 0, float z_ = 0, float w_ = 0) : x( x_ ), y( y_ ), z( z_ ), w( w_ ) {}
// 		explicit vec4f( vec3f& vec, float w_ = 0) : x( vec.x ), y( vec.y ), z( vec.z ), w( w_ ) {}
// 
// 		void getData(float* data)
// 		{
// 			data[0] = x;
// 			data[1] = y;
// 			data[2] = z;
// 			data[3] = w;
// 		}
// 
// 		vec4f& operator+=( const vec4f& other )
// 		{
// 			x += other.x;
// 			y += other.y;
// 			z += other.z;
// 			w += other.w;
// 
// 			return *this;
// 		}
// 
// 		vec4f operator+( vec4f& other ) const
// 		{
// 			vec4f result( *this );
// 			result += other;
// 			return result;
// 		}
// 
// 		float operator[]( int i ) const
// 		{
// 			assert( 0 <= i && i < 3 );
// 			return (&x) [i];
// 
// 			/*switch(i)
// 			{
// 			case 0:
// 				return x;
// 			case 1:
// 				return y;
// 			case 2:
// 				return z;
// 			default:
// 				assert(false);
// 				return x;
// 			}*/
// 		}
// 
// 		float& operator[]( int i ) 
// 		{
// 			switch(i)
// 			{
// 			case 0:
// 				return x;
// 			case 1:
// 				return y;
// 			case 2:
// 				return z;
// 			default:
// 				assert(false);
// 				return x;
// 			}
// 		}
// 
// 		vec4f& operator-=( const vec4f& other )
// 		{
// 			x -= other.x;
// 			y -= other.y;
// 			z -= other.z;
// 			w -= other.w;
// 
// 			return *this;
// 		}
// 
// 		vec4f operator-( vec4f& other ) const
// 		{
// 			vec4f result( *this );
// 			result -= other;
// 			return result;
// 		}
// 
// 		bool operator==( const vec4f& other )
// 		{
// 			bool res = false;
// 			if (x == other.x && y == other.y && z == other.z && w == other.w) res = true;
// 
// 			return res;
// 		}
// 
// 		vec4f& operator*=( float scalar )
// 		{
// 			x *= scalar;
// 			y *= scalar;
// 			z *= scalar;
// 			w *= scalar;
// 
// 			return *this;
// 		}
// 
// 		vec4f operator*( float scalar ) const
// 		{
// 			vec4f result( *this );
// 			result *= scalar;
// 			return result;
// 		}
// 
// 		vec4f& operator/=( float scalar )
// 		{
// 			x /= scalar;
// 			y /= scalar;
// 			z /= scalar;
// 			w /= scalar;
// 
// 			return *this;
// 		}
// 
// 		vec4f operator/( float scalar ) const
// 		{
// 			vec4f result( *this );
// 			result /= scalar;
// 			return result;
// 		}
// 	};
// 
// 	struct vec2f
// 	{
// 		float x, y;
// 		explicit vec2f( float x_ = 0, float y_ = 0) : x( x_ ), y( y_ ) {}
// 
// 		bool operator==( const vec2f& other )
// 		{
// 			bool res = false;
// 			if (x == other.x && y == other.y) res = true;
// 			
// 			return res;
// 		}
// 	};

	inline unsigned int packColorCoordinates( unsigned int r, unsigned int g, unsigned int b, unsigned int a = 0xff )
	{
		// little endian
		return ( a << 24 ) | ( b << 16 ) | ( g << 8 ) | ( r );
	}

	

	template< typename T >
	GLuint CreateBuffer( const std::vector<T>& vertices, GLenum bufferType = GL_ARRAY_BUFFER )
	{
		// create a temporary VBO
		GLuint result;
		glGenBuffers(1, &result);
		glBindBuffer( bufferType, result );	// TODO GL_ELEMENT_ARRAY_BUFFER

		glBufferData( bufferType, sizeof(T) * vertices.size(), &( vertices.front() ), GL_STATIC_DRAW );

		return result;
	}

	///// move from main class
	void buildProjectionMatrix(float *m, float fov, float aspect, float znear, float zfar);
	
	void matrixTranspose(float* mat);
	
	void matrixMult(const float* lhs, const float* rhs, float* result);
	void matrixMult33(const float* lhs, const float* rhs, float* result);
	
	void fromEulerAngles( float pitch, float yaw, float roll, float* res );
	GLuint LoadMemoryTexture( unsigned char* pixels, int width, int height );
	GLuint LoadTexture( const char* szFilePath );
	void DrawOrigin (float length);
	wchar_t* CharToWChar(char* ch);
	wchar_t* CharToWChar(const char* ch);
	bool magic_testIntersectionSphereTriangle(const vec3f &center, float radius, const vec3f &pt0, const vec3f &pt1, const vec3f &pt2, float *dist);

	inline float DotProduct(vec3f v1, vec3f v2)
	{
		return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z);
	}

	inline float lenSq(vec3f vec)
	{
		return vec.x*vec.x + vec.y*vec.y + vec.z*vec.z;
	}


	template<typename T>
	inline T clamp( T a, T min, T max )
	{
		return a < min ? min : ( a > max ? max : a );
	}

	inline bool IsKeyDown( int virtualKey )
	{
		return ( ::GetAsyncKeyState( virtualKey ) & 0x8000 ) == 0x8000;
	}

	void DrawVBO( GLuint idVBO, GLsizei nVBOElements, GLuint idIBO, GLsizei nIBOElements );
	void DrawVBOTangent( GLuint idVBO, GLsizei nVBOElements, GLuint idIBO, GLsizei nIBOElements );

	inline float RandInRange(float min, float max)
	{
		return min + ( max - min ) * (rand() / static_cast< float >( RAND_MAX ));
	}


	template<typename to, typename from>to lexical_cast(from const &x) 
	{
	  std::stringstream os;
	  to ret;

	  os << x;
	  os >> ret;

	  return ret;  
	}


}